package Thread;//自己实现时间戳


import java.util.PriorityQueue;

// 通过 implements 或 持有的 成员变量 都可以实现时间戳
// 封装任务（类似一个闹钟提醒事项）
class MyTimerTask implements Comparable<MyTimerTask> {
    // 要执行的任务内容（比如：播放铃声、发送消息等）
    private Runnable runnable;
    // 任务执行的时间戳（绝对时间，单位：毫秒）
    private long time;

    // 构造函数：把普通任务包装成定时任务
    public MyTimerTask(Runnable runnable, long delay) {
        this.runnable = runnable;  // 保存要执行的任务
        // 计算绝对时间：当前时间 + 延迟时间 = 任务触发时间
        this.time = System.currentTimeMillis() + delay;
    }

    // 执行任务（相当于到时间后触发闹钟）
    public void run() {
        runnable.run();  // 调用实际任务的run方法
    }

    // 获取任务执行时间（用于判断什么时候该触发）
    public long getTime() {
        return time;
    }

    //通过Comparable接口实现compareTo方法      -> 来指定比较的大小关系

    @Override
    public int compareTo(MyTimerTask o) {
        //按照时间来进行比较 ->按照小堆
        return (int) (this.time - o.time); //实现小根堆
       // return (int) (o.time - this.time);
    }
}

//保存任务         ->   使用优先级队列,便于后续取任务时根据时间先后顺序来执行
class MyTimer{
    private PriorityQueue<MyTimerTask>taskQueue  = new PriorityQueue<>();

    // 创建锁对象 -> 也可以直接使用this作为锁对象
    private Object locker = new Object();

    public MyTimer(){
        //执行任务
        //下面创建的这个线程,负责不停地扫描上面队列队首元素,来确定是否要执行任务
        Thread t =new Thread(() -> {
            try{
                while(true){
                    //加锁
                    synchronized (locker){
                        //如果队列为空就不执行后面的poll操作
                        if(taskQueue.size() == 0){
                            locker.wait();//队列为空 -> wait
                        }
                        //从队列中获取元素
                        MyTimerTask task =  taskQueue.peek();
                        //获取当前时间
                        long curTime = System.currentTimeMillis();
                        //取出元素以后,再判定时间:当前时间是否大于队首元素的任务时间
                        if(curTime >= task.getTime()){
                            //时间到啦,要执行当前任务
                            task.run();
                            //执行完当前任务以后,再将其删除
                            taskQueue.poll();
                        }else{
                            //时间没到 , 为了减少等待时间,我们可以按照时间差进行等待
                            locker.wait(task.getTime() - curTime);   // 时间没到 -> wait
                        }
                    }
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        });
        t.start();
    }

    public void schedule(Runnable runnable , long delay){       //2存储任务
        //先加锁,再入队列
        synchronized (locker){
            MyTimerTask task = new MyTimerTask(runnable , delay);
            taskQueue.offer(task);//添加任务
            //执行完上面的添加任务,再进行唤醒锁
            locker.notify();    //上面的两个 wait 都是由schedule这个方法里面的notify唤醒的
            //唤醒完之前的 wait 以后,重新判定队首元素是否要执行,以及等待的时间是多少
        }
    }
    
    
}



public class Demo37 {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();
        myTimer.schedule(new Runnable(){
            @Override
            public void run(){
                System.out.println("hello 3000");
            }

        },3000);


        myTimer.schedule(new Runnable(){
            @Override
            public void run(){
                System.out.println("hello 2000");
            }

        },2000);

        myTimer.schedule(new Runnable(){
            @Override
            public void run(){
                System.out.println("hello 1000");
            }

        },1000);
    }
}
